/*
 * hw_serials_num.c
 *
 *  Created on: Feb 24, 2018
 *      Author: xin
 */
#define LOG_TAG "SN"

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>   //ifreq
#include <errno.h>
#include <string.h>
#include <time.h>
#include "hw_serials_num.h"
#include "agent_core_lib.h"
#include "iniparser.h"
#include "cJSON.h"



bool is_valid_mac(char * mac)
{
    if(strlen(mac)< 17)
        return false;

    if(mac[2] !=':' || mac[5] !=':' || mac[8] !=':' || mac[11] !=':')
        return false;
    else
        return true;
}


bool getMAC_sysfile(char * mac_buffer, int buffer_len)
{
    FILE *f = fopen("/sys/class/net/eth0/address", "rb");
    if (f == NULL)
    {
        WALOG("open /sys/class/net/eth0/address fail.err=%d\n", errno);
        return false;
    }
    if(fgets(mac_buffer, (buffer_len),f) != NULL)
    {
        WALOG("/sys/class/net/eth0/address: %s", mac_buffer);
        fclose(f);

        int len = strlen(mac_buffer);

        if(len >0 && mac_buffer[len-1] == '\n')
            mac_buffer[len-1] = 0;

        return (is_valid_mac(mac_buffer));
    }

    fclose(f);
    return false;
}



bool make_sn_from_mac(char * mac, char *sn)
{
    int i;
    if(strlen(mac)< 17)
        return false;

    for(i=0;i<6;i++)
    {
        strncpy(sn+i*2, mac+i*3, 2);
    }

    return true;
}


bool write_mac_sn_file(char * path, char *mac)
{
    char sn[100] = {0};

    make_full_dir(DEFAULT_IAGENT_STORE_PATH);

    if(!make_sn_from_mac(mac, sn))
        return false;

    FILE *fmac = fopen(path, "w+");
    if(fmac != NULL)
    {
        fputs(sn, fmac);
        fclose(fmac);

        return true;
    }
    else
    {
        WALOG("write_mac_sn_file: open [%s] failed. (%d)]n", path, errno);
    }

    return false;
}


bool gen_MAC_file_ifconfig(char * filepath)
{
    char cmd[1024];
    char tmp_path[1024];
    bool ret = false;

    snprintf(tmp_path, sizeof(tmp_path), "%s.tmp", filepath);

    snprintf(cmd, sizeof(cmd), "rm -f %s", tmp_path);
    int r = system(cmd);

    snprintf(cmd, sizeof(cmd),
            "ifconfig | grep Ethernet | awk '$0 ~ /HWaddr/ { print $5 }' > %s",
            tmp_path);

    r |= system(cmd);
    (void) r;

    if(0 != access(tmp_path,R_OK|W_OK ))
    {
        return false;
    }
    else
    {
        FILE *f = fopen(tmp_path, "rb");
        char line[100];
        if (f == NULL)
        {
            return false;
        }
        line[0] = 0;

        while(fgets(line, sizeof(line),f) && line[0])
        {
            if(is_valid_mac(line))
            {
                if(write_mac_sn_file(filepath, line))
                {
                    ret = true;
                    break;
                }
            }
            line[0] = 0;
        }

        fclose(f);
        remove(tmp_path);
    }

    return ret;
}


bool mac_eth0(char * mac_buffer, int buffer_len)
{
#define HWADDR_len 6
    int s;
    struct ifreq ifr;
    s = socket(AF_INET, SOCK_DGRAM, 0);
    if(s == -1)
        return false;

    strcpy(ifr.ifr_name, "eth0");
    if(ioctl(s, SIOCGIFHWADDR, &ifr) <0)
    {
        close(s);
        return false;
    }

    unsigned char * mac = (unsigned char *)ifr.ifr_hwaddr.sa_data;

    snprintf(mac_buffer, buffer_len, "%02x-%02x-%02x-%02x-%02x-%02x",mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

    return true;
}


bool check_hw_serials_file(char * path, char * serials_buffer, int buffer_len)
{
    char MAC[100];
    if(path == NULL)
        path = DEFAULT_SN_FILE_NAME;

    if(0 != access(path,R_OK|W_OK ))
    {
        if(getMAC_sysfile(MAC, sizeof(MAC)) && write_mac_sn_file(path, MAC))
        {
            INFO("check_hw_serials_file: generate the sn file from sys mac file\n");
        }

        else if(gen_MAC_file_ifconfig(path))
        {
            INFO("check_hw_serials_file: generate the sn file from ifconfig mac file\n");
        }
        else if(mac_eth0(MAC, sizeof(MAC)))
        {
            if(!write_mac_sn_file(path, MAC))
                return false;
            else
                INFO("check_hw_serials_file: generate the sn file from eth0 mac file\n");
        }
        else
        {
            // set a random number as HW serials
            srand(time(0) + getpid());
            unsigned int r = (unsigned int)rand();
            FILE *fmac = fopen(path, "w+");
            if(fmac != NULL)
            {
                unsigned char * p = (unsigned char *) &r;
                fprintf(fmac, "%08x", r);
                r = (unsigned int)rand();
                fprintf(fmac, "%02x%02x", p[0], p[1]);
                fclose(fmac);
                INFO("check_hw_serials_file: generate the sn file from random num\n");
            }
            else
            {
                WALOG("check_hw_serials_file: failed to generate the sn file: %s, error: %d", path, errno);
                return false;
            }

        }
    }

    FILE *f = fopen(path, "rb");
    if (f == NULL)
    {
        WALOG("check_hw_serials_file: open %s.err=%d\n",path, errno);
        return false;
    }

    while(fgets(serials_buffer, (buffer_len),f) != NULL)
    {
        // skip the blank lines
        if(serials_buffer[0] != 0 && serials_buffer[0] != '\n')
        {
            int len = strlen(serials_buffer);
            if(serials_buffer[len-1] == '\n')
                serials_buffer[len-1] = 0;
            fclose(f);
            return true;
        }
    }

    fclose(f);
    int r = remove(path);

    WALOG("check_hw_serials_file: cant get the valid sn from [%s]. tried to delete it, result: %d.\n", path, r);

    return false;

}


bool load_device_id(char * id, int len)
{
    dictionary  *ini;
    bool ret = false;
    char * path_env = (char *)DEFAULT_DI_FILE_NAME;
    ini = iniparser_load(path_env);
    if (NULL == ini)
    {
        WARNING("Unable to load log config. err: %d. path: %s\n", errno, path_env);
        return false;
    }
    else
    {
        const char *value = iniparser_getstring(ini, "i-agent:agent_id", NULL);
        if (value && (*value) && (strlen(value) < len))
        {
            strcpy(id, value);
            ret = true;
        }
        iniparser_freedict(ini);
    }
    return ret;
}


bool save_device_id(char *id)
{
    if (access(DEFAULT_IAGENT_STORE_PATH, R_OK) != 0)
        make_full_dir(DEFAULT_IAGENT_STORE_PATH);

    return set_ini_key((char*)DEFAULT_DI_FILE_NAME, "i-agent:agent_id", id);
}


cJSON * load_factory_file(char *file)
{

	cJSON * j = NULL;
	FILE *f;long len;char *data;

	f=fopen(file,"rb");

	if(f == NULL)
		return NULL;
	fseek(f,0,SEEK_END);
	len=ftell(f);
	if(len == 0)
		return NULL;

	fseek(f,0,SEEK_SET);
	data=(char*)malloc(len+1);
	size_t n = fread(data,1,len,f);
	data[len]='\0';
    if(n >0 ) data[n]='\0';
	j = cJSON_Parse(data);
	free(data);
	fclose(f);

	return j;
}


bool load_factory_key(char **name, char **key)
{
    char fname[256];
    char *path_env = getenv("IAGENT_STORE_PATH");
    if (path_env == NULL)
        path_env = DEFAULT_FACTORY_KEY_FILE_NAME;
    else
    {
        snprintf(fname, sizeof(fname), "%s/factory.key", path_env);
        path_env = fname;
    }
    cJSON *cfg = load_factory_file(path_env);
    if (cfg == NULL)
    {
        return false;
    }

    if (cJSON_HasObjectItem(cfg, "name") && cJSON_HasObjectItem(cfg, "value"))
    {
        cJSON *item = cJSON_GetObjectItem(cfg, "name");
        if (item->type != cJSON_String)
            return false;
        char *valuestring = item->valuestring;
        if (valuestring)
            *name = strdup(valuestring);
        else
            return false;
        item = cJSON_GetObjectItem(cfg, "value");
        if (item->type != cJSON_String)
        {
            free(*name);
            *name = NULL;
            return false;
        }
        valuestring = item->valuestring;
        if (valuestring)
        {
            *key = strdup(valuestring);
            return true;
        }
        else
        {
            free(*name);
            *name = NULL;
            return false;
        }
    }
    else
        return false;

}